/*
	Copyright (C) 2010 2011 Rémi Thebault

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <procycle/cycle.h>

namespace PC {

	Cycle::Cycle()
	:	MacroTask(this, 0)
	{}

	Cycle::~Cycle() {
	}



	Task* Cycle::resolvAbsoluteTask(QString tbsId) {
		QString cTbsId = this->tbsId();
		if (!tbsId.startsWith(cTbsId)) {
			return 0;
		}
		if (tbsId.length() == cTbsId.length()) {
			return this;
		}
		tbsId = tbsId.right(tbsId.length()-cTbsId.length()-1);
		return resolvTask(tbsId);
	}



	void Cycle::preSchedule() {
		foreach(Task* t, m_tasks) {
			t->preSchedule();
		}
	}


	void Cycle::schedule() {

		preSchedule();

		reset();

		// forward scheduling
		QList<Task*> begins = beginTasks();
		foreach(Task* t, begins) {
			t->forwSchedule(0);
		}
		foreach(Task* t, m_tasks) {
			if (t->finish() > m_finish) {
				m_finish = t->finish();
			}
		}
		m_length = m_finish - m_start;
		m_lateFinish = m_finish;
		m_lateStart = m_start;

		// backward scheduling
		QList<Task*> ends = endTasks();
		foreach(Task* t, ends) {
			t->backwSchedule(0);
		}
		foreach(Task* t, m_tasks) {
			if (t->lateStart() < m_lateStart) {
				m_lateStart = t->lateStart();
			}
		}

		postSchedule();

		emit scheduled();
	}


	void Cycle::postSchedule() {
		foreach(Task* t, m_tasks) {
			t->postSchedule();
		}
	}


	Link* Cycle::createLink(Task* from, Task* to, Link::Type type, int lag) {
		Link* l = new Link(from, to, type, lag);
		m_links.append(l);
		l->registerToTasks();
		emit linkCreated(l);
		schedule();
		return l;
	}


	void Cycle::deleteLink(PC::Link *link) {
		if (m_links.contains(link)) {
			emit linkWillDelete(link);
			link->taskFrom()->unregisterOutLink(link);
			link->taskTo()->unregisterInLink(link);
			m_links.removeAll(link);
			delete link;
			schedule();
		}
	}

	void Cycle::connectTask(Task *task) {
		if (task != this) {
			QObject::connect(task, SIGNAL(taskValueChanged(PC::Task*,PC::Task::Data)),
							 this, SIGNAL(taskValueChanged(PC::Task*,PC::Task::Data)));
			QObject::connect(task, SIGNAL(taskTimeChanged(PC::Task*)),
							 this, SIGNAL(taskTimeChanged(PC::Task*)));

			MacroTask* mt = dynamic_cast<MacroTask*>(task);
			if (mt) {
				QObject::connect(mt, SIGNAL(taskCreated(PC::MacroTask*, PC::Task*)),
								 this, SIGNAL(taskCreated(PC::MacroTask*, PC::Task*)));
				QObject::connect(mt, SIGNAL(taskWillDelete(PC::Task*)),
								 this, SIGNAL(taskWillDelete(PC::Task*)));
				QObject::connect(mt, SIGNAL(taskMoved(PC::MacroTask*,PC::Task*,int,int)),
									this, SIGNAL(taskMoved(PC::MacroTask*,PC::Task*,int,int)));
				QObject::connect(mt, SIGNAL(taskTransformed(PC::MacroTask*,int)),
									this, SIGNAL(taskTransformed(PC::MacroTask*,int)));
			}
		}
	}


	void Cycle::disconnectTask(Task* task) {
		if (task != this) {
			QObject::disconnect(task, SIGNAL(taskValueChanged(PC::Task*,PC::Task::Data)),
								this, SIGNAL(taskValueChanged(PC::Task*,PC::Task::Data)));
			QObject::disconnect(task, SIGNAL(taskTimeChanged(PC::Task*)),
								this, SIGNAL(taskTimeChanged(PC::Task*)));

			MacroTask* mt = dynamic_cast<MacroTask*>(task);
			if (mt) {
				QObject::disconnect(mt, SIGNAL(taskCreated(PC::MacroTask*,PC::Task*)),
									this, SIGNAL(taskCreated(PC::MacroTask*,PC::Task*)));
				QObject::disconnect(mt, SIGNAL(taskWillDelete(PC::Task*)),
									this, SIGNAL(taskWillDelete(PC::Task*)));
				QObject::disconnect(mt, SIGNAL(taskMoved(PC::MacroTask*,PC::Task*,int,int)),
									this, SIGNAL(taskMoved(PC::MacroTask*,PC::Task*,int,int)));
				QObject::disconnect(mt, SIGNAL(taskTransformed(PC::MacroTask*,int)),
									this, SIGNAL(taskTransformed(PC::MacroTask*,int)));
			}
		}
	}


	void Cycle::connectLink(Link* link) {
		QObject::connect(link, SIGNAL(changed(PC::Link*)), this, SIGNAL(linkChanged(PC::Link*)));
	}

	void Cycle::disconnectLink(Link *link) {
		QObject::disconnect(link, SIGNAL(changed(PC::Link*)), this, SIGNAL(linkChanged(PC::Link*)));
	}


	void
	Cycle::loadFromElement(const QDomElement& element) {
		MacroTask::loadFromElement(element);
		m_links.clear();
		QList<QDomElement> el = directElementsByTagName(element, "link");
		foreach(QDomElement e, el) {
			Link* l = new Link(this);
			l->loadFromElement(e);
			m_links.append(l);
		}
	}


	QDomElement
	Cycle::saveToElement(QDomDocument& doc) const {
		QDomElement element = MacroTask::saveToElement(doc);
		foreach(Link* l, m_links) {
			element.appendChild(l->saveToElement(doc));
		}
		return element;
	}


}
